﻿using System.Runtime.CompilerServices;

namespace zijian666.Data;

public static class DbInterceptors
{
    private static readonly List<IDbInterceptor> CACHE = new();
    private static IDbInterceptor[] INTERCEPTORS = [];

    /// <summary>
    /// 注册全局诊断拦截器
    /// </summary>
    public static void AddDiagnostic() => Add(DbDiagnosticInterceptor.Instance);

    /// <summary>
    /// 注册全局拦截器
    /// </summary>
    [MethodImpl(MethodImplOptions.Synchronized)]
    public static void Add(IDbInterceptor interceptor)
    {
        if (CACHE.Contains(interceptor))
        {
            return;
        }
        CACHE.Add(interceptor);
        INTERCEPTORS = CACHE.ToArray();
    }

    /// <summary>
    /// 移除已注册的拦截器
    /// </summary>
    [MethodImpl(MethodImplOptions.Synchronized)]
    public static bool Remove(IDbInterceptor interceptor)
    {
        if (CACHE.Remove(interceptor))
        {
            INTERCEPTORS = CACHE.ToArray();
            return true;
        }
        return false;
    }


    internal static ValueTask<DbCommandCreatedInterceptorContext?> OnCommandCreated(Func<DbCommandCreatedInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.CommandCreated(context));


    internal static ValueTask<DbExecuteReaderInterceptorContext?> OnQueryStarting(Func<DbExecuteReaderInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.QueryStarting(context));


    internal static ValueTask<DbExecuteReaderInterceptorContext?> OnQueryExecuted(Func<DbExecuteReaderInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.QueryExecuted(context));


    internal static ValueTask<DbExecuteNonQueryInterceptorContext?> OnNonQueryExecuted(Func<DbExecuteNonQueryInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.NonQueryExecuted(context));


    internal static ValueTask<DbTransactionInterceptorContext?> OnTransactionStarting(Func<DbTransactionInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.TransactionStarting(context));


    internal static ValueTask<DbTransactionInterceptorContext?> OnTransactionCommitted(Func<DbTransactionInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.TransactionCommitted(context));

    internal static ValueTask<DbTransactionInterceptorContext?> OnTransactionRollbacked(Func<DbTransactionInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.TransactionRollbacked(context));

    internal static ValueTask<DbConnectionInterceptorContext?> OnConnectionStateChanging(Func<DbConnectionInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.ConnectionStateChanging(context));


    internal static ValueTask<DbExceptionContext?> OnExceptionOccurred(Func<DbExceptionContext> fn,
        [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0)
        => OnEvent(() =>
        {
            var context = fn();
            context.Skip = context.Exception.Data.Contains(nameof(OnExceptionOccurred));
            context.Exception.Data[nameof(OnExceptionOccurred)] = true;
            context.File = file;
            context.Member = member;
            context.Line = line;
            return context;
        }, (interceptor, context) => context.Skip ? ValueTask.CompletedTask : interceptor.ExceptionOccurred(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityInserting(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityInserting(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityUpdateing(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityUpdateing(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityDeleting(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityDeleting(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityInserted(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityInserted(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityUpdated(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityUpdated(context));

    internal static ValueTask<DbEntityInterceptorContext?> OnEntityDeleted(Func<DbEntityInterceptorContext> fn)
        => OnEvent(fn, (interceptor, context) => interceptor.EntityDeleted(context));

    internal static CancellationToken GetCancellationToken(CancellationToken cancellationToken)
    {
        if (INTERCEPTORS.Length == 0)
        {
            return cancellationToken;
        }
        var tokens = INTERCEPTORS.Select(x => x.CancellationToken).Append(cancellationToken).Where(x => x.CanBeCanceled).ToArray();
        if (tokens.Length is 0)
        {
            return cancellationToken;
        }
        if (tokens.Length is 1)
        {
            return tokens[0];
        }
        return CancellationTokenSource.CreateLinkedTokenSource(tokens).Token;
    }


    private static async ValueTask<TContext?> OnEvent<TContext>(Func<TContext> fn, Func<IDbInterceptor, TContext, ValueTask> action)
        where TContext : DbInterceptorContext
    {
        if (INTERCEPTORS.Length == 0)
        {
            return null;
        }
        var context = fn();
        foreach (var interceptor in INTERCEPTORS)
        {
            await action(interceptor, context);
        }
        context.Cts.Token.ThrowIfCancellationRequested();
        return context;
    }
}
